#pragma once
#include <string>
#include <iostream>
#include "Socket.hpp"
#include <memory>
#include <jsoncpp/json/json.h>
#include <functional>
/*实现一个自定义的网络版本的计算机器*/

// client->server  客户向服务器发起请求报文
using namespace SocketModule;
// 约定好的各个字段的含义，本质就是约定好协议
class Request
{
public:
    Request() // 无参构造
    {
    }
    Request(int x, int y, char oper) // 函数的重载
        : _x(x), _y(y), _oper(oper)
    {
    }
    std::string Serialize() // 序列化
    {
        // 将我们的结构化字段转换为字符串字段
        Json::Value root;
        root["x"] = _x;
        root["y"] = _y;
        root["oper"] = _oper;

        Json::FastWriter writer;
        std::string s = writer.write(root); // 将我们root对象直接转换为字符串
        return s;                           // 到这里我们就弯沉了序列化了
    }
    bool Deserialize(std::string &in) // 反序列化
    {
        Json::Value root;
        Json::Reader reader;
        bool ok = reader.parse(in, root); // 将收到的字符串in反序列化到root中
        if (ok)
        {
            _x = root["x"].asInt(); // 将字符串反序列化到我们结构化字段中
            _y = root["y"].asInt();
            _oper = root["oper"].asInt();
            return true;
        }
        return ok;
    }
    ~Request()
    {
    }
    int X() { return _x; }
    int Y() { return _y; }
    char Oper() { return _oper; }

private:
    int _x;
    int _y;
    char _oper; //+  -  *  /  %    _x  oper  _y->10+20
};

// server->client   服务器进行数据的反馈操作

// 如何做序列化和反序列化
// 1、我们自己写
// 2、使用现成的方案--->JSON->jsoncpp
class Response
{
public:
    Response()
    {
    }

    Response(int result, int code)
        : _result(result), _code(code)
    {
    }

    std::string Serialize() // 序列化
    {
        // 将我们的结构化字段转换为字符串字段
        //  "10" "20" '+' ->以空格为分隔符
        Json::Value root;
        root["result"] = _result;
        root["code"] = _code;

        Json::FastWriter writer;
        return writer.write(root);
    }
    bool Deserialize(std::string &in) // 反序列化
    {
        //"10" "20" '+'   ->以空格为分隔符  ->10 20 +
        Json::Value root;
        Json::Reader reader;
        bool ok = reader.parse(in, root); // 将收到的字符串in反序列化到root中
        if (ok)
        {
            // 从root中进行数据的提取操作
            _result = root["result"].asInt(); // 将字符串反序列化到我们结构化字段中
            _code = root["code"].asInt();
            return true;
        }
        return ok;
    }

    ~Response()
    {
    }
    void SetResult(int res)
    {
        _result = res;
    }
    void SetCode(int code)
    {
        _code = code;
    }
    void ShowResult()
    {
        std::cout << "结果是:" << _result << "[" << _code << "]" << std::endl;
    }

private:
    int _result; // 运算结果  ,无法区分清楚应答是计算结果还是异常值

    int _code; // 状态码 0 成功 1 失败  约定就是协议
    // 当我们的_code状态是0的时候，我们的结果才是可信的
};

// 协议（基于TCP协议）需要解决两个问题：
// request和response必须有序列化和反序列化功能
// 你必须保证，读取的时候读到完整的请求（TCP要考虑，UDP不需要考虑）

using func_t = std::function<Response(Request &req)>; // 定义一个函数指针类型
// 这里有一个函数，要求将req变成response，所以这里的返回值是Response

// 就是从client->server  到 server->client  这个过程，我们需要进行序列化和反序列化的操作

const std::string sep = "\r\n";
class Protocol
{
public:
    Protocol()
    {
    }

    Protocol(func_t func)
        : _func(func)
    {
    }
    // 在我们基础的json串的前面加上json传的有效长度，并且加上分割符
    std::string Encode(const std::string jsonstr) // 传过来一个json串
    {
        // 50\r\n{"x":10,"y":20,"oper":"+"}\r\n
        std::string len = std::to_string(jsonstr.size()); // 将这个json串的长度转换为字符串
        std::string package = len + sep + jsonstr + sep;  // 应用层封装报头
        return package;
    }

    // 收到了这样的json串   50\r\n{"x":10,"y":20,"oper":"+"}\r\n

    // 判断报文完整性
    // 如果包含至少一个完整请求，提取他。并从缓冲区中移除他，方便处理下一个
    bool Decode(std::string &buffer, std::string *package) // package是需要处理的字符串
    {
        ssize_t pos = buffer.find(sep); // 先找到第一个分隔符
        if (pos == std::string::npos)   // 如果没有找到分隔符，说明缓冲区中没有完整的请求报文
        {
            return false; // 让调用方继续从内核中读取数据
        }
        // 走到这里说明这里存在一个完整的请求报文
        std::string package_len_str = buffer.substr(0, pos); // 提取报文长度字符串
        int package_len_int = std::stoi(package_len_str);    // 将报文长度字符串转换为整数

        // buffer一定有长度，但是一定有完整的报文么
        int target_len = package_len_str.size() + package_len_int + 2 * sep.size(); // 这个是这个buffer中包含的完整的请求报文的最小长度
        // package_len_int是中间数据的长度，package_len_str.size()是长度字符串的长度，sep.size()是分隔符的长度
        if (buffer.size() < target_len) // 如果缓冲区中没有完整的请求报文
        {
            return false; // 让调用方继续从内核中读取数据
        }
        // 走到这里说明缓冲区中有完整的请求报文
        // buffer中至少有一个完整的报文
        // 这里的pos现在是指向一开始的分隔符的下标的，加上分隔符的长度就是我们需要处理数据的开始位置了
        *package = buffer.substr(pos + sep.size(), package_len_int); // 提取请求报文

        // 从开头处将这个第一个报文移除
        buffer.erase(0, target_len); // 从缓冲区中移除这个完整的请求报文

        // 然后就开始进行处理下一个json串了
        return true;
    }

    // 从套接字sock中读取，客户端是client，服务器是server
    void GetRequest(std::shared_ptr<Socket> &sock, InetAddr &client) // 获取请求报文
    {
        // 读取
        std::string buffer_queue; // 缓冲区队列
        while (true)
        {
            int n = sock->Recv(&buffer_queue); // 从套接字中读取数据,将数据读取到inbuffer中
            if (n > 0)                         // 读成功了
            {
                std::cout << "-------------------request_buffer:-----------" << std::endl;
                std::cout << buffer_queue << std::endl;
                std::cout << "------------------------------------------" << std::endl;
                std::string json_package;
                // 1、解析报文，提取完整的json请求，如果不完整的话，就让服务器继续读取
                while (Decode(buffer_queue, &json_package))//将我们收到的请求都进行处理操作了
                     // 将提取的报文数据存储在json_package中
                {
                    std::cout << "-------------------request_json:-----------" << std::endl;
                    std::cout << json_package << std::endl;
                    std::cout << "------------------------------------------" << std::endl;

                    std::cout << "-------------------request_buffer:-----------" << std::endl;
                    std::cout << buffer_queue << std::endl;
                    std::cout << "------------------------------------------" << std::endl;
                    // 1、走到这里，这里我们一定拿到了完整的报文
                    //{"x":10,"y":20,"oper":"+"}
                    // 2、请求json串，我们需要进行反序列化

                    LOG(LogLevel::DEBUG)<<client.StringAddr()<<" 请求"<<json_package;
                    Request req;                             // 客户给 服务端发送的请求对象
                    bool ok = req.Deserialize(json_package); // 反序列化,将json串反序列化到req对象中
                    // 进行完成了反序列化操作之后，我们的结构体中的_x,_y,_oper已经有了值了
                    if (!ok) // 反序列化失败
                    {
                        continue; // 说明这个报文我们不受理，我们继续让我们的服务器读取下一个
                    }
                    // 3、到这里我们一定得到了内部属性已经被设置了的req了

                    // 通过req得到result，进行计算操作,计算功能是不属于协议的，计算功能是我们网络服务需要完成的功能的
                    // 我们如何进行结算和我们的协议是没有任何的关系的，我们这里主要是进行解析报文、反序列化的操作
                    // 所以计算结果的请求我们需要外部进行处理操作
                    Response resp = _func(req); // 计算功能的调用
                    // 客户端给服务器的请求给这个函数，这个客户端给服务器的请求已经被我们进行清晰的解析操作了
                    // 我们通过这个函数进行请求的解析操作

                    // resp就是我们服务器给客户端的结果了

                    // 4、我们需要对这个结果进行序列化操作
                    std::string json_str = resp.Serialize(); // 序列化然后就得到了字符串了

                    // 5、根据协议规定，我们得给我们的返回报文前面加上长度以及分割符
                    // 不管是请求还是应答，都应该是一样的，双方都应该保证报文的完整性

                    // 添加自定义长度
                    std::string send_str = Encode(json_str); // 封装报文

                    // 6、直接发送
                    sock->Send(send_str);
                    // send里面的内容就是往_sockfd中写入message的内容
                }
            }
            else if (n == 0)
            {
                // 客户端退出了
                LOG(LogLevel::INFO) << "client" << client.StringAddr() << "Quit!";
                break;
            }
            else
            {
                LOG(LogLevel::WARING) << "client" << client.StringAddr() << "Recv error!";
                break;
            }
        }
    }
    bool GetResponse(std::shared_ptr<Socket> &client, std::string &resp_buffer, Response *resp) // 获取应答报文
    {

        // resp_buffer应答的缓冲区
        //  面相字节流的，你怎么保证你的客户端读到的网络字符串是一个完整的请求呢
        while (true)
        {
            int n = client->Recv(&resp_buffer); // 从套接字中读取数据,将数据读取到resp_str中
            if (n > 0)
            {
                std::cout << "-------------------resp_buffer:-----------" << std::endl;
                std::cout << resp_buffer << std::endl;
                std::cout << "------------------------------------------" << std::endl;
                // 读取成功了
                std::string json_package;
                // 1、解析报文，提取完整的json请求，如果不完整的话，就让服务器继续读取
                bool ret = Decode(resp_buffer, &json_package); // 将提取的报文数据存储在json_package中
                if (ret == false)
                {
                    // 说明你给我发的流式字符串中不存在一个完整的报文
                    continue;
                }
                std::cout << "-------------------resoose_json:-" << std::endl;
                std::cout << json_package << std::endl;
                std::cout << "--------------------------------" << std::endl;
                // 走到这里我们能保证一定能拿到一个完整的JSON串
                resp->Deserialize(json_package); // 反序列化,将json串反序列化到resp对象中
                return true;
            }
            else if (n == 0) // 客户端退出的情况
            {
                std::cout << "client quit!" << std::endl;
                return false;
            }
            else
            {
                std::cout << " recv error!" << std::endl;
                return false;
            }
        }
    }
    std::string BuildRequestString(int x, int y, char oper)
    {
        // 1、构建一个完整的请求
        Request req(x, y, oper); // 构建一个请求对象---client->server

        // 2、序列化这个请求对象
        std::string json_req = req.Serialize(); // 序列化

        // 2、1
        std::cout << "----------------json_req:------------------" << std::endl;
        std::cout << json_req << std::endl;
        std::cout << "--------------------------------------------" << std::endl;

        // 3、根据协议规定，我们得给我们的请求报文前面加上长度以及分割符
        return Encode(json_req); // 封装报文
    }
    ~Protocol()
    {
    }

private:
    // 因为我们用的是多进程
    //  Request _req;
    //  Response _resp;

    func_t _func; // 计算功能的函数指针
};